package de.tu_cottbus.portal.security.ldap;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.OperationNotSupportedException;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.PagedResultsControl;
import javax.naming.ldap.PagedResultsResponseControl;

import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.model.User;
import com.liferay.portal.security.ldap.LDAPSettingsUtil;
import com.liferay.portal.security.ldap.PortalLDAPImporterImpl;
import com.liferay.portal.security.ldap.PortalLDAPUtil;
import com.liferay.portal.service.UserLocalServiceUtil;
import com.liferay.portal.util.PrefsPropsUtil;
import com.liferay.portal.util.PropsValues;

public class CustomPortalLDAPImporterImpl extends PortalLDAPImporterImpl {

	@Override
	protected User importUser(long companyId, Attributes attributes,
			Properties userMappings, Properties userExpandoMappings,
			Properties contactMappings, Properties contactExpandoMappings,
			String password) throws Exception {

		User user = super.importUser(companyId, attributes, userMappings,
				userExpandoMappings, contactMappings, contactExpandoMappings,
				password);

		// Die Gruppe wurde bereits erstellt und ihre ID ist in
		// user.getUserGroupIds() gespeichert. Also muss der Benutzer nur noch
		// dieser Gruppe zugewiesen werden.

		for (long userGroupId : user.getUserGroupIds()) {
			UserLocalServiceUtil.addUserGroupUsers(userGroupId,
					new long[] { user.getUserId() });
		}

		return user;
	}

	@Override
	protected void importFromLDAPByUser(long ldapServerId, long companyId,
			LdapContext ldapContext, Properties userMappings,
			Properties userExpandoMappings, Properties contactMappings,
			Properties contactExpandoMappings, Properties groupMappings)
			throws Exception {

		int ldapPage = 1;
		int userCount = 0;
		int errors = 0;
		byte[] cookie = new byte[0];

		_log.info("Starting LDAP import.");

		while (cookie != null) {
			List<SearchResult> searchResults = new ArrayList<SearchResult>();

			String userMappingsScreenName = userMappings.getProperty(
					"screenName").toLowerCase();

			cookie = getUsers(ldapServerId, companyId, ldapContext, cookie, 0,
					new String[] { userMappingsScreenName }, searchResults);

			for (SearchResult searchResult : searchResults) {

				try {
					Attributes userAttributes = PortalLDAPUtil
							.getUserAttributes(ldapServerId, companyId,
									ldapContext, PortalLDAPUtil
											.getNameInNamespace(ldapServerId,
													companyId, searchResult));

					User user = importUser(companyId, userAttributes,
							userMappings, userExpandoMappings, contactMappings,
							contactExpandoMappings, StringPool.BLANK);

					_log.debug("Importing user #" + userCount + ": "
							+ user.getScreenName());
				} catch (Exception e) {
					errors++;
					_log.error("Unable to import user#" + userCount + ": "
							+ searchResult, e);
				}

				userCount++;
			}

			_log.info("Imported page " + ldapPage++ + ".");
		}

		_log.info("LDAP import of " + --ldapPage + " pages and " + userCount
				+ " users finished. There were " + errors + " errors.");
	}

	private byte[] getUsers(long ldapServerId, long companyId,
			LdapContext ldapContext, byte[] cookie, int maxResults,
			String[] attributeIds, List<SearchResult> searchResults)
			throws Exception {

		String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);

		String baseDN = PrefsPropsUtil.getString(companyId,
				PropsKeys.LDAP_BASE_DN + postfix);
		String userFilter = PrefsPropsUtil.getString(companyId,
				PropsKeys.LDAP_IMPORT_USER_SEARCH_FILTER + postfix);

		return getUsers(companyId, ldapContext, cookie, maxResults, baseDN,
				userFilter, attributeIds, searchResults);
	}

	private byte[] getUsers(long companyId, LdapContext ldapContext,
			byte[] cookie, int maxResults, String baseDN, String userFilter,
			String[] attributeIds, List<SearchResult> searchResults)
			throws Exception {

		return searchLDAP(ldapContext, cookie, maxResults, baseDN, userFilter,
				attributeIds, searchResults);
	}

	private byte[] searchLDAP(LdapContext ldapContext, byte[] cookie,
			int maxResults, String baseDN, String filter,
			String[] attributeIds, List<SearchResult> searchResults)
			throws NamingException, IOException {

		SearchControls searchControls = new SearchControls(
				SearchControls.SUBTREE_SCOPE, maxResults, 0, attributeIds,
				false, false);

		try {
			if (cookie != null) {
				if (cookie.length == 0) {
					ldapContext
							.setRequestControls(new Control[] { new PagedResultsControl(
									PropsValues.LDAP_PAGE_SIZE,
									Control.CRITICAL) });
				} else {
					ldapContext
							.setRequestControls(new Control[] { new PagedResultsControl(
									PropsValues.LDAP_PAGE_SIZE, cookie,
									Control.CRITICAL) });
				}

				NamingEnumeration<SearchResult> enu = ldapContext.search(
						baseDN, filter, searchControls);

				while (enu.hasMoreElements()) {
					searchResults.add(enu.nextElement());
				}

				enu.close();
				return _getCookie(ldapContext.getResponseControls());
			}
		} catch (OperationNotSupportedException onse) {
			ldapContext.setRequestControls(null);

			NamingEnumeration<SearchResult> enu = ldapContext.search(baseDN,
					filter, searchControls);

			while (enu.hasMoreElements()) {
				searchResults.add(enu.nextElement());
			}

			enu.close();
		} finally {
			ldapContext.setRequestControls(null);
		}
		return null;
	}

	private byte[] _getCookie(Control[] controls) {
		if (controls == null) {
			return null;
		}

		for (Control control : controls) {
			if (control instanceof PagedResultsResponseControl) {
				PagedResultsResponseControl pagedResultsResponseControl = (PagedResultsResponseControl) control;
				return pagedResultsResponseControl.getCookie();
			}
		}
		return null;
	}

	private static Log _log = LogFactoryUtil
			.getLog(CustomPortalLDAPImporterImpl.class);

}
